/***********************************************************
 * bike_data_test - CUnit test of the code in the bike_data
 * header/C files
 * *******************************************************/

#include <stdio.h>
#include <string.h>
#include "CUnit/Basic.h"
#include "../Includes/bike_data.h"

// initialize the first suite
int init_suite1(void){
  return 0;
}

// clean up the first suite
int clean_suite1(void){
  return 0;
}

// test setting WheelSecondsPerRotation through update

void testWheelSecondsPerRotation(void){
  BikeData Data;
  initializeBikeData(&Data);
  int i;

  for(i = 1; i < 1000; i++){
	  update(&Data,i*10,1,2,23);
	  CU_ASSERT(getWheelSecondsPerRotation(&Data) == i*10);
    float returnAvg = getAvgWheelSecondsPerRotation(&Data);
    float calAvg = (float)(i+1)/2*10;
    //printf("returnAvg = %f calAvg = %f\n",returnAvg,calAvg);
	  //CU_ASSERT((getAvgWheelSecondsPerRotation(&Data) - (float)(i+1)/2*10)<1);
    CU_ASSERT((returnAvg-calAvg)*100/calAvg < 0.001);
    CU_ASSERT((calAvg - returnAvg)*100/calAvg < 0.001);
	  CU_ASSERT(getMaxWheelSecondsPerRotation(&Data) == i*10);
	  CU_ASSERT(getMinWheelSecondsPerRotation(&Data) == 10);
  }
}

void testWheelSecondsPerRotation2(void){
  BikeData Data;
  initializeBikeData(&Data);
  int i;

  for(i = 1000; i > 0; i--){
	  update(&Data,i*10,1,2,23);
	  CU_ASSERT(getWheelSecondsPerRotation(&Data) == i*10);
    //printf("\n i = %d and avg = %f and avgCal = %f\n",i,getAvgWheelSecondsPerRotation(&Data),(float)(i+1)/2*10);
    float calAvg = (float)(1000*1001/2 - i*(i-1)/2)/(1001 - i)*10;
    float returnAvg = getAvgWheelSecondsPerRotation(&Data);
    CU_ASSERT((returnAvg-calAvg)*100/calAvg < 0.005);
    CU_ASSERT((calAvg - returnAvg)*100/calAvg < 0.005);
	  CU_ASSERT(getMaxWheelSecondsPerRotation(&Data) == 10000);
	  CU_ASSERT(getMinWheelSecondsPerRotation(&Data) == i*10);
  }
}

// test setting PedalSecondsPerRotation through update

void testPedalSecondsPerRotation(void){
  BikeData Data;
  initializeBikeData(&Data);
  int i;

  for(i = 1; i < 1000; i++){
	  update(&Data,0,i*10,0,23);
	  CU_ASSERT(getPedalSecondsPerRotation(&Data) == i*10);
    //printf("\n i = %d and avg = %f and avgCal = %f\n",i,getAvgPedalSecondsPerRotation(&Data),(float)(i+1)/2*10);
    float returnAvg = getAvgPedalSecondsPerRotation(&Data);
    float calAvg = (float)(i+1)/2*10;
    CU_ASSERT((returnAvg-calAvg)*100/calAvg < 0.005);
    CU_ASSERT((calAvg - returnAvg)*100/calAvg < 0.005);
	  CU_ASSERT(getMaxPedalSecondsPerRotation(&Data) == i*10);
	  CU_ASSERT(getMinPedalSecondsPerRotation(&Data) == 10);
  }
}

void testPedalSecondsPerRotation2(void){
  BikeData Data;
  initializeBikeData(&Data);
  int i;

  for(i = 1000; i > 0; i--){
	  update(&Data,0,i*10,0,23);
	  CU_ASSERT(getPedalSecondsPerRotation(&Data) == i*10);
    //printf("\n i = %d and avg = %f and avgCal = %f\n",i,getAvgPedalSecondsPerRotation(&Data),(float)(i+1)/2*10);
    float calAvg = (float)(1000*1001/2 - i*(i-1)/2)/(1001 - i)*10;
    float returnAvg = getAvgPedalSecondsPerRotation(&Data);
    CU_ASSERT((returnAvg-calAvg)*100/calAvg < 0.005);
    CU_ASSERT((calAvg - returnAvg)*100/calAvg < 0.005);
	  CU_ASSERT(getMaxPedalSecondsPerRotation(&Data) == 10000);
	  CU_ASSERT(getMinPedalSecondsPerRotation(&Data) == i*10);
  }
}

// test setting HeartRateBeatsPerSecond through update

void testHeartRateBeatsPerSecond(void){
  BikeData Data;
  initializeBikeData(&Data);
  int i;

  for(i = 1; i < 1000; i++){
	  update(&Data,0,0,i*10,23);
	  CU_ASSERT(getHeartRateBeatsPerSecond(&Data) == i*10);
    //printf("\n i = %d and avg = %f and avgCal = %f\n",i,getAvgHeartRateBeatsPerSecond(&Data),(float)(i+1)/2*10);
    float returnAvg = getAvgHeartRateBeatsPerSecond(&Data);
    float calAvg = (float)(i+1)/2*10;
    CU_ASSERT((returnAvg-calAvg)*100/calAvg < 0.005);
    CU_ASSERT((calAvg - returnAvg)*100/calAvg < 0.005);
	  CU_ASSERT(getMaxHeartRateBeatsPerSecond(&Data) == i*10);
	  CU_ASSERT(getMinHeartRateBeatsPerSecond(&Data) == 10);
  }
}

void testHeartRateBeatsPerSecond2(void){
  BikeData Data;
  initializeBikeData(&Data);
  int i;

  for(i = 1000; i > 0 ; i--){
	  update(&Data,0,0,i*10,23);
	  CU_ASSERT(getHeartRateBeatsPerSecond(&Data) == i*10);
    //printf("\n i = %d and avg = %f and avgCal = %f\n",i,getAvgHeartRateBeatsPerSecond(&Data),(float)(i+1)/2*10);
    float returnAvg = getAvgHeartRateBeatsPerSecond(&Data);
    float calAvg = (float)(1000*1001/2 - i*(i-1)/2)/(1001 - i)*10;
    CU_ASSERT((returnAvg-calAvg)*100/calAvg < 0.005);
    CU_ASSERT((calAvg - returnAvg)*100/calAvg < 0.005);
	  CU_ASSERT(getMaxHeartRateBeatsPerSecond(&Data) == 10000);
	  CU_ASSERT(getMinHeartRateBeatsPerSecond(&Data) == i*10);
  }
}

// test setting calories through update

void testCalories(void){
  BikeData Data;
  initializeBikeData(&Data);
  setPersonalData(&Data, 20, 180, MALE);
  int i;

  //Test for a male
  for(i = 1; i < 1000; i++){
	  update(&Data,0,0,10,23);
    float result = getCalories(&Data);
    float calculated = i*31.49324291E-3;
    CU_ASSERT((result - calculated)*100/calculated < 0.005);
    CU_ASSERT((calculated - result)*100/calculated < 0.005);
    //printf("\nreturned = %f calculated = %f\n",result,calculated);
  }

  //Testfor a female
  resetCalories(&Data);
  setPersonalData(&Data, 45, 120, FEMALE);
  for(i = 1; i < 1000; i++){
	  update(&Data,0,0,10,23);
    float result = getCalories(&Data);
    float calculated = i*23.6502190E-3;
    CU_ASSERT((result - calculated)/calculated < 0.001);
    //printf("\nreturned = %f calculated =
    //%f\n",result,calculated));
  }
}

// Test the reset, increment, and get for the
// WheelRevolutionCount
void testWheelRevolutionCount(void){
  int result;
  BikeData Data;
  initializeBikeData(&Data);

  int i = 0;
  for(i = 0; i<1000; i++){
    incrementWheelRevolutionCount(&Data);
    result = getWheelRevolutionCount(&Data);
    CU_ASSERT(result == (int)(i +1))
  }

  resetWheelRevolutionCount(&Data);
  result = getWheelRevolutionCount(&Data);
  CU_ASSERT(result == (int)0);
}

// Test the reset, increment, and get for the
// PedalRevolutionCount
void testPedalRevolutionCount(void){
  int result;
  BikeData Data;
  initializeBikeData(&Data);

  int i = 0;
  for(i = 0; i<1000; i++){
    incrementPedalRevolutionCount(&Data);
    result = getPedalRevolutionCount(&Data);
    CU_ASSERT(result == (int)(i +1))
  }

  resetPedalRevolutionCount(&Data);
  result = getPedalRevolutionCount(&Data);
  CU_ASSERT(result == (int)0);
}

// Test the reset, increment, and get for the
// HeartRateBeatsCount
void testHeartRateBeatsCount(void){
  int result;
  BikeData Data;
  initializeBikeData(&Data);

  int i = 0;
  for(i = 0; i<1000; i++){
    incrementHeartRateBeatsCount(&Data);
    result = getHeartRateBeatsCount(&Data);
    CU_ASSERT(result == (int)(i +1))
  }

  resetHeartRateBeatsCount(&Data);
  result = getHeartRateBeatsCount(&Data);
  CU_ASSERT(result == (int)0);
}

// Test the incrementElapseTimeBy function

void testIncrementElapsedTimeBy(void){
  int result;
  BikeData Data;
  initializeBikeData(&Data);

  //First check that incrementing and the modulo works
  int i = 0;
  for(i = 0; i<10; i++){
    incrementElapsedTimeBy(&Data, 10000);
    result = getElapsedTimeMilSec(&Data);
    int correct = ((i+1)*10000)%60000;
    CU_ASSERT(result == correct);
  }

  //Test result of Minute and Hour
  result = getElapsedTimeMin(&Data);
  CU_ASSERT(result == (int)1);
  result = getElapsedTimeHour(&Data);
  CU_ASSERT(result == (int)0);
  float result2 = getElapsedTime(&Data)/1000;
  CU_ASSERT(result2 == (float)100);
}

// Test the incrementElapseTimeBy function

void testIncrementElapsedTimeBy2(void){
  int result;
  float result2,correct;
  BikeData Data;
  initializeBikeData(&Data);

  int i,j;
  for(j = 1; j < 61; j++){
	  for(i = 1; i < 61; i++){
	    result = getElapsedTimeMilSec(&Data);
	    CU_ASSERT(result == (int)0);

	    result = getElapsedTimeMin(&Data);
	    CU_ASSERT(result == (i-1));

      result2 = getElapsedTime(&Data)/1000;
      CU_ASSERT_EQUAL(result2 ,(j-1)*3600+(i-1)*60);

	    incrementElapsedTimeBy(&Data, 60000);
	  }
  result = getElapsedTimeHour(&Data);
  CU_ASSERT_EQUAL(result,j);

  result2 = getElapsedTime(&Data)/1000;
  CU_ASSERT_EQUAL(result2,j*3600);
  }
}

// Test the set and get for personal data

void testPersonalData(void){
  int result;
  bool result2;
  BikeData Data;
  initializeBikeData(&Data);

  setPersonalData(&Data, 34, 180, MALE);

  result = getAge(&Data);
  CU_ASSERT(result == 34);

  result = getWeight(&Data);
  CU_ASSERT(result == 180);

  result = getGender(&Data);
  CU_ASSERT(result == MALE);

  setPersonalData(&Data, 18, 100, FEMALE);

  result = getAge(&Data);
  CU_ASSERT(result == 18);

  result = getWeight(&Data);
  CU_ASSERT(result == 100);

  result = getGender(&Data);
  CU_ASSERT(result == FEMALE);

  setPersonalData(&Data, 0, 0, FEMALE);

  result = getAge(&Data);
  CU_ASSERT(result == 0);

  result = getWeight(&Data);
  CU_ASSERT(result == 0);

  result = getGender(&Data);
  CU_ASSERT(result == FEMALE);
}

// Test of the Wheel Rotatoins Per Hour

void testWheelRotationsPerHour(void){
  float results;
  float cal_results;

  results = WheelRotationsPerHour(6);
  CU_ASSERT(results == (float) 600);

  results = WheelRotationsPerHour(2.5);
  CU_ASSERT(results == (float) 1440);

  results = WheelRotationsPerHour(100000);
  CU_ASSERT(results == (float) 0.036);
  
  results = WheelRotationsPerHour(0.001);
  //printf("\n%f\n",results);
  cal_results = 3600*1000;
  CU_ASSERT((results - cal_results)*100/cal_results < 0.001 );
  CU_ASSERT((results - cal_results)*100/cal_results > -0.001 );
}

// Test the Pedal RPM

void testPedalRPM(void){
  float results;
  float cal_results;

  results = PedalRPM(6);
  CU_ASSERT(results == (float) 10);

  results = PedalRPM(2.5);
  CU_ASSERT(results == (float) 24);

  results = PedalRPM(100000);
  CU_ASSERT(results == (float) 0.0006);
  
  results = PedalRPM(0.001);
  //printf("\n%f\n",results);
  cal_results = 60*1000;
  CU_ASSERT((results - cal_results)*100/cal_results < 0.001 );
  CU_ASSERT((results - cal_results)*100/cal_results > -0.001 );
}

// Test the HeartRateBPM

void testHeartRateBPM(void){
  float results;

  results = HeartRateBPM(6);
  CU_ASSERT(results == (float) 360);

  results = HeartRateBPM(1.3);
  CU_ASSERT(results == (float) 78);

  results = HeartRateBPM(0);
  CU_ASSERT(results == (float) 0);

  results = HeartRateBPM(100000);
  CU_ASSERT(results == (float) 60*100000);
}

// Test the HeartRatePercent

void testHeartRatePercent(void){
  float results;
  float cal_results;

  results = HeartRatePercent((float)150/60,20);
  CU_ASSERT(results == (float) 75);

  results = HeartRatePercent((float)0,60);
  CU_ASSERT(results == (float) 0);

  results = HeartRatePercent((float) 40/60,60);
  //printf("\n%f",results);
  cal_results = 25;
  CU_ASSERT((results - cal_results)*100/cal_results < 0.001);
  CU_ASSERT((results - cal_results)*100/cal_results > -0.001);

  results = HeartRatePercent((float) 80/60,60);
  //printf("\n%f",results);
  cal_results = 50;
  CU_ASSERT((results - cal_results)*100/cal_results < 0.001);
  CU_ASSERT((results - cal_results)*100/cal_results > -0.001);

  results = HeartRatePercent((float)120/60,60);
  //printf("\n%f",results);
  cal_results = 75;
  CU_ASSERT((results - cal_results)*100/cal_results < 0.001);
  CU_ASSERT((results - cal_results)*100/cal_results > -0.001);
  
  results = HeartRatePercent((float)160/60,60);
  //printf("\n%f",results);
  cal_results = 100;
  CU_ASSERT((results - cal_results)*100/cal_results < 0.001);
  CU_ASSERT((results - cal_results)*100/cal_results > -0.001);
}

// Test the DistanceTraveledMiles

void testDistanceTraveledMiles(void){
  float results;

  results = DistanceTraveledMiles(10000);
  CU_ASSERT(results == (float) 13.135787);

  results = DistanceTraveledMiles(350000);
  CU_ASSERT(results == (float) 459.752563);

  results = DistanceTraveledMiles(0);
  CU_ASSERT(results == (float) 0);

}

// Test of the Speed MPH

void testSpeedMPH(void){
  float results;

  results = SpeedMPH(6);
  CU_ASSERT(results == (float) 0.7881472202);

  results = SpeedMPH(0.2);
  CU_ASSERT(results == (float) 23.644417);
}

/* The main() function for setting up and running the tests.
 *  * Returns a CUE_SUCCESS on successful running, another
 *   * CUnit error code on failure.
 *    */
int main()
{
   CU_pSuite pSuite = NULL;

   /* initialize the CUnit test registry */
   if (CUE_SUCCESS != CU_initialize_registry())
      return CU_get_error();

   /* add a suite to the registry */
   pSuite = CU_add_suite("Suite_1", init_suite1, clean_suite1);
   if (NULL == pSuite) {
      CU_cleanup_registry();
      return CU_get_error();
   }

   /* add the tests to the suite */
   /* NOTE - ORDER IS IMPORTANT - MUST TEST fread() AFTER
 * fprintf() */
   if ((NULL == CU_add_test(pSuite, "testWheelSecondsPerRotation", testWheelSecondsPerRotation))||
       (NULL == CU_add_test(pSuite, "testWheelSecondsPerRotation2", testWheelSecondsPerRotation2))||
       (NULL == CU_add_test(pSuite, "testPedalSecondsPerRotation", testPedalSecondsPerRotation))||
       (NULL == CU_add_test(pSuite, "testPedalSecondsPerRotation2", testPedalSecondsPerRotation2))||
       (NULL == CU_add_test(pSuite, "testHeartRateBeatsPerSecond", testHeartRateBeatsPerSecond))||
       (NULL == CU_add_test(pSuite, "testHeartRateBeatsPerSecond2", testHeartRateBeatsPerSecond2))||
       (NULL == CU_add_test(pSuite, "testCalories", testCalories))||
       (NULL == CU_add_test(pSuite, "testWheelRevolutionCount", testWheelRevolutionCount))||
       (NULL == CU_add_test(pSuite, "testPedalRevolutionCount", testPedalRevolutionCount))||
       (NULL == CU_add_test(pSuite, "testHeartRateBeatsCount", testHeartRateBeatsCount))||
       (NULL == CU_add_test(pSuite, "testIncrementElapsedTimeBy", testIncrementElapsedTimeBy))||
       (NULL == CU_add_test(pSuite, "testIncrementElapsedTimeBy2", testIncrementElapsedTimeBy2))||
       (NULL == CU_add_test(pSuite, "testPersonalData", testPersonalData))||
       (NULL == CU_add_test(pSuite, "testWheelRotationsPerHour", testWheelRotationsPerHour))||
       (NULL == CU_add_test(pSuite, "testPedalRPM", testPedalRPM))||
       (NULL == CU_add_test(pSuite, "testHeartRateBPM", testHeartRateBPM))||
       (NULL == CU_add_test(pSuite, "testHeartRatePercent", testHeartRatePercent))||
       (NULL == CU_add_test(pSuite, "testDistanceTraveledMiles", testDistanceTraveledMiles))||
       (NULL == CU_add_test(pSuite, "testSpeedMPH", testSpeedMPH)))
   {
      CU_cleanup_registry();
      return CU_get_error();
   }

   /* Run all tests using the CUnit Basic interface */
   CU_basic_set_mode(CU_BRM_VERBOSE);
   CU_basic_run_tests();
   CU_cleanup_registry();
   return CU_get_error();
}

